package org.springblade.modules.auth.utils;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.secure.exception.SecureException;
import org.springblade.core.secure.provider.IClientDetails;
import org.springblade.core.secure.provider.IClientDetailsService;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.*;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

public class SecureOutUtil extends AuthUtil {
    private static final String CLIENT_ID = "client_id";
    private static IClientDetailsService clientDetailsService;
    private static JwtProperties jwtProperties;

    public SecureOutUtil() {
    }

    private static IClientDetailsService getClientDetailsService() {
        if (clientDetailsService == null) {
            clientDetailsService = (IClientDetailsService) SpringUtil.getBean(IClientDetailsService.class);
        }

        return clientDetailsService;
    }

    private static JwtProperties getJwtProperties() {
        if (jwtProperties == null) {
            jwtProperties = (JwtProperties) SpringUtil.getBean(JwtProperties.class);
        }

        return jwtProperties;
    }

    public static TokenInfo createJWT(Map<String, String> user, String audience, String issuer, String tokenType) throws UnsupportedEncodingException {
        String[] tokens = extractAndDecodeHeader();

        assert tokens.length == 2;

        String clientId = tokens[0];
        String clientSecret = tokens[1];
        IClientDetails clientDetails = clientDetails(clientId);
        if (!validateClient(clientDetails, clientId, clientSecret)) {
            throw new SecureException("client authentication failed, please check the header parameters");
        } else {
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
            byte[] apiKeySecretBytes = Base64.getDecoder().decode(JwtUtil.getBase64Security());
            Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
            JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken").setIssuer(issuer).setAudience(audience).signWith(signatureAlgorithm, signingKey);
            user.forEach(builder::claim);
            builder.claim("client_id", clientId);
            long expireMillis = (long) (315360000);
            long expMillis = nowMillis + expireMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
            TokenInfo tokenInfo = new TokenInfo();
            tokenInfo.setToken(builder.compact());
            tokenInfo.setExpire(315360000);
            if (getJwtProperties().getState()) {
                String tenantId = (String) user.get("tenant_id");
                String userId = (String) user.get("user_id");
                JwtUtil.addAccessToken(tenantId, userId, tokenInfo.getToken(), 315360000);
            }

            return tokenInfo;
        }
    }

//    public static long getExpire() {
//        Calendar cal = Calendar.getInstance();
//        cal.add(6, 1);
//        cal.set(11, 3);
//        cal.set(13, 0);
//        cal.set(12, 0);
//        cal.set(14, 0);
//        return cal.getTimeInMillis() - System.currentTimeMillis();
//    }

    public static String[] extractAndDecodeHeader() throws UnsupportedEncodingException {
        String header = ((HttpServletRequest) Objects.requireNonNull(WebUtil.getRequest())).getHeader("Authorization");
        header = Func.toStr(header).replace("Basic%20", "Basic ");
        if (!header.startsWith("Basic ")) {
            throw new SecureException("no client information in request header");
        } else {
            byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);

            byte[] decoded;
            try {
                decoded = Base64.getDecoder().decode(base64Token);
            } catch (IllegalArgumentException var5) {
                throw new RuntimeException("failed to decode basic authentication token");
            }

            String token = new String(decoded, Charsets.UTF_8_NAME);
            int index = token.indexOf(":");
            if (index == -1) {
                throw new RuntimeException("invalid basic authentication token");
            } else {
                return new String[]{token.substring(0, index), token.substring(index + 1)};
            }
        }

    }

    public static String getClientIdFromHeader() throws UnsupportedEncodingException {
        String[] tokens = extractAndDecodeHeader();

        assert tokens.length == 2;

        return tokens[0];
    }

    private static IClientDetails clientDetails(String clientId) {
        return getClientDetailsService().loadClientByClientId(clientId);
    }

    private static boolean validateClient(IClientDetails clientDetails, String clientId, String clientSecret) {
        if (clientDetails == null) {
            return false;
        } else {
            return StringUtil.equals(clientId, clientDetails.getClientId()) && StringUtil.equals(clientSecret, clientDetails.getClientSecret());
        }
    }
}
